using System.Text;
using Google.Protobuf;
using UnityEngine;

namespace Core.Net
{
    public class Frame
    {
        protected ByteBuf payload;
        protected bool end;
        private static readonly byte[] secret = Encoding.UTF8.GetBytes("t4UDM%2Q");
        public const int HeadLength = 4 + BodyHead; //包长4+body头
        public const int BodyHead = 2+8+4+4+4; // 秘钥长度2+秘钥8+crc校验码4+msgId4+protoType4= 22
        public int FrameLen { get; private set; } //包的总长度，包括包头和包尾
        //RPCID
        public int RPCID = 0;
        //消息ID
        public int MSGID = 0;
        //处理的时间
        public int CRC32 = 0;
        public int DealTimeTick { get; private set; }
        public bool IsConsumed = false;
        //是否为发送完后就自动销毁释放内存
        public bool IsSendAutoConsumed = false;
        protected Frame()
        {
            DealTimeTick = NetHelper.GetEnvironmentTickCount();
            IsConsumed = false;
        }
        /// <summary>
        /// 创建一个总长为len+headlen的帧,作为客户端，建议浪费点空间也比用不完重新分配好
        /// </summary>
        /// <param name="len"></param>
		public Frame(int len)
        {
            DealTimeTick = NetHelper.GetEnvironmentTickCount();
            //默认长度至少要比头长,否则缓冲区初始值就不够
            len = len < 0 ? 0 : len;
            this.payload = new ByteBuf(len + HeadLength);
            payload.WriterIndex(0);
            IsConsumed = false;
        }
        /**
		 * 获取缓冲区
		 **/
        public ByteBuf GetData()
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗");}
            return payload;
        }

        public Frame PutSecret()
        {
            return PutBytes(secret);
        }
        

        /**
		 * 写入一个字节
		 **/
        public Frame PutBool(bool b)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteByte(b ? (byte)1 : (byte)0);
            return this;
        }

        /**
		 * 写入一个字节
		 **/
        public Frame PutByte(byte c)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteByte(c);
            return this;
        }
        /**
		 * 写入一些字节
		 **/
        public Frame PutBytes(ByteBuf src)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end) 
                payload.WriteBytes(src);
            return this;
        }
        /**
         * 写入一些字节
         */
        public Frame PutBytes(byte[] src)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteBytes(src);
            return this;
        }

        /**
        * 写入一些字节
        */
        public Frame PutBytes(byte[] src, int readIndex, short len)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteBytes(src, readIndex, len);
            return this;
        }
        ///**
        // * 写入加密
        // */
        //public Frame PutBytes(byte[] src, byte[] ks)
        //{
        //    if (!end)
        //    {
        //        xor(src, ks);
        //        payload.WriteBytes(src);
        //    }
        //    return this;
        //}
        /**
        * 写入长整形
        **/
        public Frame PutFloat(float s)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteFloat(s);
            return this;
        }

        /**
		 * 写入长整形
		 **/
        public Frame PutLong(long s)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteLong(s);
            return this;
        }
        /**
		 * 写入整形
		 **/
        public Frame PutInt(int s)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteInt(s);
            return this;
        }
        /**
		 * 写入短整型
		 **/
        public Frame PutShort(int s)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteShort((short)s);
            return this;
        }
        /**
		 * 写入一个字符串
		 **/
        public Frame PutUTF8(string s)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteUTF8(s);
            return this;
        }
        /**
		 * 写入一个字符串
		 **/
        public Frame PutString(string s, byte[] ks)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
            {
                byte[] content = Encoding.UTF8.GetBytes(s.ToCharArray());
                this.payload.WriteShort((short)content.Length);
                xor(content, ks);
                this.payload.WriteBytes(content);
            }
            return this;
        }

        public Frame PutVector2(UnityEngine.Vector2 v2)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteVector2(v2);
            return this;
        }

        public Frame PutVector3(UnityEngine.Vector3 v3)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteVector3(v3);
            return this;
        }

        public Frame PutVector4(UnityEngine.Vector4 v4)
        { 
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteVector4(v4);
            return this;
        }

        public Frame PutQuaternion(UnityEngine.Quaternion quation)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
                payload.WriteQuaternion(quation);
            return this;
        }


        /**
		 * 写入一个可序列化的对象
		 **/
        public Frame PutObject<T>(T obj) where T : IMessage<T>
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (!end)
            {
                this.payload.WriteObject<T>(obj);
            }
            return this;
        }

        public void PutSeparator()
        {
            
        }

        /**
		 * 封包
		 **/
        public virtual void End()
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (end) return;
            ByteBuf bb = payload;
            var reader = bb.ReaderIndex();
            var writer = bb.WriterIndex();
            bb.WriterIndex(reader);
            
            //消息长度
            var msgLen = writer - reader - HeadLength; //数据长度 去掉包长
            //整包长度-包长4
            int packLen = msgLen + HeadLength - 4;
            bb.WriteInt(packLen);
            
            //写入秘钥
            bb.WriteBytes(secret);
            
            //保存用于CRC校验的读取位，不包含crc和msgId
            var crcStart = bb.WriterIndex()+8;
            //写入CRC校验   
            var crc32 = bb.CalculateCheckSum(crcStart, msgLen+4);
            bb.WriteInt(crc32);
            
            // 写入msgId
            bb.WriteInt(MSGID);
            //计算包的总长
            FrameLen = writer - reader;
            end = true;
            bb.ReaderIndex(reader);
            bb.WriterIndex(writer);
        }
        
        

        /// <summary>
        /// 重整为可以重发的包
        /// </summary>
	    public Frame SetStatusToResend()
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            //已经发送过的包
            if (end)
            {
                payload.ResetReaderIndex();
                payload.ResetWriterIndex();
                FrameLen = 0;
                end = false;
            }
            return this;
        }

        /**
		 * 是否已经封包
		 **/
        public bool IsEnd()
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            return end;
        }
        /**
		 * 设置end标示
		 **/
        public void SetEnd(bool e)
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (e)
            {
                this.End();
            }
            else
            {
                this.end = e;
            }
        }

        public static Frame ReBuildFrameFromBuf(ByteBuf buf)
        {
            var f = new Frame();
            f.payload = buf;
            f.IsConsumed = false;
            if (f.ReBuildFrame())
            {
                return f;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 重新使用Buf数据Frame内容,如果校验不通过则返回false;
        /// </summary>
	    public bool ReBuildFrame()
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (payload == null)
            {
                return false;
            }
            FrameLen = payload.WriterIndex() - payload.ReaderIndex();

            payload.ReaderIndex(0);
            int len = payload.ReadInt();

            byte[] secretB = payload.ReadBytes();
            for (int i = 0; i < secret.Length; i++)
            {
                if (secret[i] != secretB[i])
                {
                    return false;
                }
            }//秘钥

            //TODO 解密内容 
            //if (IsEncryptiong)
            //{
                //payload.Xor(4 - 1, len + 4);
            //}
            //TODO 设置加密标签为未加密 

            //重建包头
            //ServerID = payload.ReadShort();
            //SessionID = payload.ReadLong(); 
            //判断版本
            //var ver = payload.ReadShort();
            //if (ver != Version) return false;

            CRC32 = payload.ReadInt();
            MSGID = payload.ReadInt();
            

            //校验CRC  
            var startIndex = payload.ReaderIndex();
            //写入CRC校验 包含消息类型4
            var crc32 = payload.CalculateCheckSum(startIndex, len - BodyHead + 4);
            if (crc32 != CRC32)
            {
                return false;
            }
            
            RPCID = payload.ReadInt();
            
            //设置封包
            end = true;
            //重置包读指针位置 
            //payload.ReaderIndex(HeadLength);
            //payload.WriterIndex(FrameLen - TypeLength);
            return true;
        }

        public ByteBuf GetContent()
        {
            if (IsConsumed) { Debug.LogError("包内容已经被消耗"); }
            if (end)
            {
                // payload.ReaderIndex(HeadLength);
                payload.WriterIndex(FrameLen);
                return payload;
            }
            return null;
        }

        //消耗掉包体
        public void Consumed()
        {
            if (payload == null) return;
            payload.StoreData();
            payload = null;
            IsConsumed = true; 
        }

        /**
	 * 取异或
	 *
	 * @param bs
	 * @param ks
	 */
        public static void xor(byte[] bs, byte[] ks)
        {
            if (ks != null && ks.Length > 0)
            {
                for (int i = 0; i < bs.Length; i++)
                {
                    bs[i] = (byte)(bs[i] ^ ks[i % ks.Length]);
                }
            }
        }
    }
}

